package org.example.service;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.example.entity.*;
import org.example.entity.vo.LoginVo;
import org.example.exception.LoginException;
import org.example.security.UserDetailServiceImpl;
import org.example.security.UserDetailsImpl;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.time.Duration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class TokenService {

    private final IUserService userService;
    private final IRoleService roleService;
    private final IMenuService menuService;
    private final StringRedisTemplate stringRedisTemplate;
    private final ObjectMapper objectMapper;
    private final RedisTemplate redisTemplate;


    private static final String LOGIN_KEY = "s54:rbac:login:";


    @SneakyThrows
    public Token createToken(LoginVo loginVo) {
        User user = userService.getOne(Wrappers.lambdaQuery(User.class).eq(User::getUserName, loginVo.getLoginName()));
        if (user == null) throw new LoginException("用户不存在");
        if (!user.getPassword().equals(loginVo.getPassword())) throw new LoginException("密码不正确");
        if (user.getStatus().equals("0")) throw new LoginException("用户未激活");
//        获取用户所有的角色
        List<Role> roleList = roleService.listByUserId(user.getUserId());

//        根据角色获取所有菜单
        List<Menu> menuList = menuService.listByRoleIds(roleList.stream().mapToLong(Role::getRoleId).boxed().toArray(Long[]::new));
//        存储到redis
        String uuid = UUID.randomUUID().toString();
        Set<String> roleName = roleList.stream().map(Role::getRoleKey).collect(Collectors.toSet());
        Set<String> perms = menuList.stream()
                .filter(menu -> StringUtils.hasText(menu.getPerms()))
                .map(Menu::getPerms).collect(Collectors.toSet());
        LoginUser loginUser = new LoginUser(roleName, perms, user);
        String json = new ObjectMapper().writeValueAsString(loginUser);
        stringRedisTemplate.opsForValue().set(LOGIN_KEY + uuid, json, Duration.ofMillis(30));
        return new Token(System.currentTimeMillis(), System.currentTimeMillis() + 1800000, uuid, user.getNickName());

    }

    public void removeToken(String token) {
        stringRedisTemplate.delete(LOGIN_KEY + token);
    }

    @SneakyThrows
    public boolean hasPerms( String perms) {
        //判断请求头中有无token
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        String token = requestAttributes.getRequest().getHeader("token");
        if (StringUtils.isEmpty(token)) return false;
        //判断redis能否找到
        String json = stringRedisTemplate.opsForValue().get(LOGIN_KEY + token);
        if (StringUtils.isEmpty(json)) return false;
        //判断是否有权限
        LoginUser loginUser = new ObjectMapper().readValue(json, LoginUser.class);
        Set<String>  permsAndRoles = new HashSet<>();
        permsAndRoles.addAll(loginUser.getPerms());
        permsAndRoles.addAll(loginUser.getRoleName());

        return loginUser.getPerms().contains(perms);
    }

    @SneakyThrows
    public Token createToken(UserDetailsImpl authenticate) {
        String uuid = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(LOGIN_KEY + uuid, authenticate, Duration.ofMillis(30));
        return new Token(System.currentTimeMillis(), System.currentTimeMillis() + 1800000, uuid, authenticate.getUser().getNickName());
    }


    @SneakyThrows
    public UserDetailsImpl getLoginUser(String token){
        return (UserDetailsImpl) redisTemplate.opsForValue().get(LOGIN_KEY+token);
    }
}
